#ifndef ModeLobby_h__
#define ModeLobby_h__

#include <GTL.h>
#include <GTLEngine.h>

#include "Game.h"

#define TILE_WIDTH 32
#define TILE_HEIGHT 32

enum
{
	GROUND_GRASS = 16*8+1,
	GROUND_LANE = 16*8,
};

enum
{
	B_NONE = 16*16-1,
	B_SPAWNER = 16*9,
	B_CORE = 16*10,
	B_TRAP = 16*11,
};

enum
{
	DIR_UP,
	DIR_UPRIGHT,
	DIR_RIGHT,
	DIR_DOWNRIGHT,
	DIR_DOWN,
	DIR_DOWNLEFT,
	DIR_LEFT,
	DIR_UPLEFT,
};

enum
{
	MOB_WALKER = 16*8+8,
	MOB_RUSHER,
	MOB_SHELL,
};

enum
{
	DAMAGE_MELEE,
	DAMAGE_PROJECTILE
};

enum
{
	STAT_SPEED,
	STAT_DAMAGE_MELEE,
	STAT_DAMAGE_PROJECTILE,
	STAT_RADIUS,

	/// Only player stats follow
	STAT_ENGINEERING,

	STAT_ALL
};

class ModeGame;

/// TGObject

struct TGObject
{
	GTL::Engine::Point Position;
	bool Selected;

	int Health, MaxHealth;
 
	float Radius;
	float Scale;
	float Rotation;

	int Life;
	int Animation, AnimationFrames, AnimationSpeed;

	bool Zombie;

	TGObject* Next, *Prev;
	static TGObject* AllObjects;

	/// Methods

	TGObject();

	int GetImage();
	GTL::Engine::Rect GetRect();

	void DoUpdate(float dt, ModeGame* game);
	void DoDraw(ModeGame* game);

	virtual void Update(float dt, ModeGame* game);
	virtual void Draw(ModeGame* game);

	void SetAnim(int start, int frames, int animtime);

	float GetRadius() const { return Radius*Scale; }

	virtual ~TGObject() {}
};

/// Creature
struct TGBuilding;

struct Creature : TGObject
{
	float Stats[STAT_ALL], StatMultipliers[STAT_ALL];

	TGBuilding* OnBuilding;

	void CreatureDamage( Creature* mob, int type );
	virtual void DoDamage( int amount );

	virtual void Update(float dt, ModeGame* game);
	virtual void SteppedOn( TGBuilding* build );

	float GetStat(int stat);

	float GetSize() { return GetStat(STAT_RADIUS)*GetRadius(); }

	Creature() : OnBuilding(0)
	{
		for (int i=0; i<STAT_ALL; i++)
		{
			Stats[i] = 1.0f;
			StatMultipliers[i] = 1.0f;
		}
		Stats[STAT_SPEED] = 100.0f;
		Stats[STAT_RADIUS] = 1.0f;
		Stats[STAT_ENGINEERING] = 1.0f;
	}
	virtual ~Creature() {}
	void RecalculateStatuses();
};

/// Player

struct Player : Creature
{
	enum
	{
		S_STANDING,
		S_WALKING,
		S_BUILDING,
		S_ATTACKING,
		S_DYING
	};
	int State;

	int FrontX, FrontY;

	Player();
	
	virtual void Update( float dt, ModeGame* game );

	void MoveInDirection( float dt, int dir );
	void Build();
	void Stand();

	virtual void SteppedOn( TGBuilding* build );

	virtual ~Player() {}
	virtual void DoDamage( int amount );
};

/// TGBuilding

struct TGBuilding : TGObject
{
	enum
	{
		STATE_BUILDING,
		STATE_WORKING,
		STATE_DESTROYED
	};

	int State;

	GTL::Engine::Point TilePos;

	int Type;

	TGBuilding() : State(STATE_BUILDING), TilePos(0.0f, 0.0f), Type(B_NONE) {
		MaxHealth = 100;
		Health = 0;
	}

	void CalcPos()
	{
		Position = TilePos*32.0f + GTL::Engine::Point(16.0f, 16.0f);
	}

	virtual ~TGBuilding() {}
	void BuildOrRepair( int amount );
};

/// Spawner

struct Spawner : TGBuilding
{
	struct Wave
	{
		int GameTime;

		struct EnemyGroup
		{
			int EnemyType;
			int EnemyCount;

			void Reflect(Reflector& ref) { ref.R(EnemyType); ref.R(EnemyCount); }
		};
		eastl::vector<EnemyGroup> Groups;

		void Reflect(Reflector& ref)
		{
			ref.R(GameTime);
			ref.R(Groups);
		}
	};
	eastl::vector<Wave> Waves;

	struct Waypoint
	{
		GTL::Engine::Point Pos;

		void Reflect(Reflector& ref) { ref.R(Pos); }
	};
	eastl::vector<Waypoint> Path;

	void Reflect(Reflector& ref)
	{
		ref.R(TilePos);
		CalcPos();

		ref.R(MaxHealth);
		if (ref.IsReading())
			Health = MaxHealth;
		
		ref.R(Path);
		
		ref.R(Waves);
	}

	Spawner()
	{
		Type = B_SPAWNER;
		SetAnim(B_SPAWNER, 1, 100);
	}

	virtual ~Spawner() {}
};

/// Trap

struct Trap : TGBuilding
{
	virtual void ModifyCreature(Creature* cr) = 0;

	Trap()
	{
		Type = B_TRAP;
		SetAnim(B_TRAP, 1, 100);
	}
	virtual ~Trap() {}
};

struct SlowTrap : Trap
{
	virtual void ModifyCreature(Creature* cr);

	SlowTrap()
	{
		SetAnim(B_TRAP+0, 1, 100);
	}
	virtual ~SlowTrap() {}
};

/// Mob

struct Mob : Creature
{
	int Type;

	eastl::vector<Spawner::Waypoint>* Path;
	int NextTargetPoint;

	Mob() : Type(MOB_WALKER), Path(0), NextTargetPoint(-1) {}

	virtual void Update(float dt, ModeGame* game);
	virtual void SteppedOn( TGBuilding* build );

	void Move(float dt);

	virtual ~Mob() {}
	void RecalculateStatuses( TGBuilding* OnBuilding );
};

/// Level

struct Level
{
	struct Tile
	{
		int Ground;
		int BuildingType;
		TGBuilding* Building;

		Tile() : Ground(GROUND_GRASS), BuildingType(B_NONE), Building(0) {}
	};

	Level() : Core(0)
	{
		
	}

	int Width, Height;
	eastl::vector<Tile> Tiles;
	TGBuilding* Core;

	Tile& GetTile(int x, int y);
	Tile& GetTile(GTL::Engine::Point p);

	void SetSize(int w, int h);
	void Draw(ModeGame* game);

	void Load(const char* filename);
	void Destroy();
	void SetBuilding( GTL::Engine::Point pos, TGBuilding* sp,  int type );
	void PlaceLanes( const eastl::vector<Spawner::Waypoint>& waypoints );
	void PlaceLane( GTL::Engine::Point p1, GTL::Engine::Point p2 );
	void PlaceLaneTile(GTL::Engine::Point p);
	bool IsValid( GTL::Engine::Point p );
};

/* Gameplay:

- spawners - destroyable machines that spawn mobs, have tons of health and have to be destroyed over multiple waves
- mobs - produced by spawners, when they hit player players gets thrown back and damaged (some invu time?), can be killed by player
- endpoint - when mobs hit endpoint they reduce it's power, when it gets down to 0 game over
- lanes - mobs move on predetermined lanes, each spawner has a different lane through the level; player can build traps, slowers and walls on lanes
	- traps - damage mobs that walk on tile, can be single or multiple use
	- slowers - slow down mobs on that tile
	- walls - can be destroyed, stop mobs
- grounds - player can build turrets and buildings on grounds that surround the lanes
	- turrets - shoot the mobs, can shoot different debuffs, etc.
	- buildings - give passive bonuses to player, can be used to buy upgrades or sell stuff
	- turrets and buildings can be repaired
- stuff - sometimes stuff spawns on the lanes, usually on the other side of the player and the mobs, this can be gold, powerups, junk, items, etc.
- waves - the game consists of a number of mob waves separated with a few seconds of free time; each wave a number of mobs is produced by each spawner
	each wave different mob types and groups are produced
- player - the player can command one of a few different heroes, each with different abilities, stats and weapons

Obstacles:
- remember you can't walk through turrets so plan your turret placement so you can travel to different parts of the level during waves
- mobs will sometimes (50% of the time?) drop stuff

*/

class ModeGame : public GTL::Engine::GameMode<TowerGame>
{
public:
	virtual int Draw(float dt);
	virtual int Start();
	virtual int Load();
	virtual int Event(GTL::Engine::GameEvent event); /// TODO: Finish me;
	virtual int Stop();
	virtual int Unload();
	virtual int Update(float dt);

	GTL::Engine::Rect GetRectForFrame( int frame );

	void DrawRect(const GTL::Engine::Rect& rect, const GTL::Engine::ColorRGBA& color, const GTL::Engine::ColorRGBA& fillcolor = GTL::Engine::ColorRGBA(0,0,0,0));
	void DrawFrameRotated( int frame, GTL::Engine::Point position, float rotation, GTL::Engine::Point scale );
	void DrawFrameSimple( int frame, GTL::Engine::Point position );
	void DrawLine( float x1, float y1, float x2, float y2, GTL::Engine::ColorRGBA color );
	GTL::Engine::Image gfx;

	GTL::Engine::Rect Viewport;

	Player* player;
	Level level;
};

inline void DetachObject(TGObject* obj)
{
	if (!obj) return;

	if (obj->Prev)
	{
		obj->Prev->Next = obj->Next;
	} else
	{
		TGObject::AllObjects = obj->Next;
	}

	if (obj->Next)
		obj->Next->Prev = obj->Prev;
}

inline void AttachObject(TGObject* obj)
{
	if (!obj) return;

	obj->Next = TGObject::AllObjects;
	if (TGObject::AllObjects)
		TGObject::AllObjects->Prev = obj;
	TGObject::AllObjects = obj;
}

template <typename T>
void DestroyObject(T*& obj) { 
	DetachObject((TGObject*)obj);

	delete obj; 
	obj = 0;
}

template <typename T> T* CreateObject() { 
	T* obj = new T; 
	AttachObject(obj);
	return obj;
}


#endif